package com.streetsmart;

/**
 * Used to track and aid the navigation of the user once he starts executing the schedule. 
 * Periodically check where the user is. Sense if there is any delay by comparing the current ocation with that of the expected location of the user.
 * If there are any delays notifies the user with notifications that allow the user to go back to the application and reschedue.
 * Also updates the state of each schedule event into the database then and there.
 * 
 */
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

import org.json.JSONException;

import com.streetsmart.main.SSMyApplication;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.os.IBinder;
import android.os.Looper;
import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech.OnInitListener;
import android.util.Log;
import com.streetsmart.RescheduleReceiver;

//public class StartScheduleNavigation extends Thread {
public class StartScheduleNavigation extends Service {

	ArrayList<ScheduleListItem> schedule = null;
	private boolean isRunning = true;
	ScheduleListItem next = null;
	long startAt;
	long reachDestinationAt;
	// boolean isDelayed = false;
	ScheduleListItem currentEvent = null;
	long currentTime = 0, previousCurrentTime = 0;
	long delay = 0;
	int currentScheduleIndex;
	double destLat, destLng;
	DBManager db = null;
	Cursor cur = null;
	Context context = null;
	double curLat, curLng;
	static Thread navigator;
	private TextToSpeech tts;
	boolean isTTSInstalled = false;

	@Override
	public void onStart(Intent intent, int startId) {
		super.onStart(intent, startId);
		Log.d("NAVIGATOR", "Starting delay sensor and navigator");
		context = SSMyApplication.getAppContext();
		db = new DBManager(context);
		isTTSInstalled = intent.getBooleanExtra("tts", false);
		this.schedule = Utilities.getSchedule();
		currentEvent = schedule.get(0);
		next = schedule.get(1);
		db.updateEventStatus(next.getEvent().getId(), "inprogress");
		startAt = currentEvent.getEvent().getEndTime().getTime();
		reachDestinationAt = next.getStartTime().getTime();
		destLat = next.getEvent().getLocation().getLatitude();
		destLng = next.getEvent().getLocation().getLongitude();
		currentScheduleIndex = 1;
		// Need to change this LATER !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
		startRunningService();
	}

	/**
	 * spawns a new thread that does the entire tracking stuff
	 */
	public void startRunningService() {
		navigator = new Thread(new Runnable() {
			public void run() {
				try {
					Looper.prepare();
					while (isRunning() == true) {
						Thread.sleep(30000);
						currentTime = Utilities.getCurrentTime();
						Log.d("NAVIGATOR", "running every 30 seconds");
						if (isCurrentGreaterThanStart(currentTime)
								&& (currentTime - previousCurrentTime >= 300000)) {
							previousCurrentTime = currentTime;
							Log.d("NAVIGATOR", "Running every 5 mins");

							// Come here if either the user answers no to
							// the
							// reschedule or
							// if there is no delay.
							if (isDestinationReached()) {
								setUpNextTarget();
								continue;
							} else {
								Log.d("NAVIGATOR",
										"Checking if there is any delay");
								long timeElapsed = Utilities.getCurrentTime()
										- startAt;
								String[] temp = new String[3];
								try {
									temp = Utilities.getDistance(new double[] {
											curLat, curLng }, new double[] {
											destLat, destLng });
								} catch (JSONException e) {
									e.printStackTrace();
								}
								long timeToReachDestination = Long
										.parseLong(temp[1]);
								long actualTimeToReach = timeElapsed
										+ timeToReachDestination;
								//long expectedTimeToReach = next.getEvent()
								//		.getTimeToReachThis();
								long expectedTimeToReach = next.getStartTime().getTime() - currentEvent.getEvent().getEndTime().getTime();
								Log.d("NAVIGATOR", "actual time:"
										+ actualTimeToReach + " expected time:"
										+ expectedTimeToReach);
								if (actualTimeToReach > expectedTimeToReach) {
									// TODO get User response by telling him
									// the
									// delay minutes and asking him for
									// reschedule.
									// TODO If the user answers yes stop
									// this
									// thread.. Give him
									// a new schedule based on current time
									// and
									// the
									// events in the database.
									// TODO You might want to update the
									// database
									// with the list of already completed
									// events
									// so
									// far before rescheduling.
									// TODO also if the event approaching is
									// a
									// Must
									// event include that in schedule or
									// intimate user.
									// TODO If user answers no go to the
									// next
									// line
									// after this if block

									// isDelayed = true;
									delay = actualTimeToReach
											- expectedTimeToReach;
									Log.d("NAVIGATOR", "Delay sensed");
									if (isTTSInstalled == true) {
										tts = new TextToSpeech(context,
												new OnInitListener() {

													public void onInit(
															int status) {
														// mTts.setLanguage(Locale.US);
													}
												});
										tts.speak(
												"You are running late by "
														+ (delay / 60000)
														+ " mins. Please check your notification bar, if you wish to reschedule",
												TextToSpeech.QUEUE_FLUSH, null);
										while (tts.isSpeaking()) {
											Thread.sleep(100);
										}
										tts.stop();
										tts.shutdown();
									}
									Intent notifyRescheduling = new Intent(
											context, RescheduleReceiver.class);
									notifyRescheduling.putExtra("RESCHEDULE",
											true);
									context.sendBroadcast(notifyRescheduling);
									Log.d("NAVIGATOR",
											"You are running late by " + delay
													/ 60000 + " mins!");
								}
							}

						}
					}
					Looper.loop();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

			/**
			 * To check if the current time is past the start time (to start
			 * tracking)
			 * 
			 * @param current
			 * @return
			 */
			public boolean isCurrentGreaterThanStart(long current) {
				long temp = current - startAt;
				if (temp > 30000) {
					return true;
				}
				return false;
			}

			/**
			 * checks if the next stop or the final stop has reached. Next stop
			 * refers to the next item in the schedule. Final destination is the
			 * last stop in the schedule which is same as the return location of
			 * the user.
			 * 
			 * @return
			 */
			public boolean isDestinationReached() {
				Log.d("NAVIGATOR", "Checking if destination reached");
				boolean result = false;
				db = new DBManager(context);
				cur = db.getMostRecentLocation();
				if (cur.getCount() > 0) {
					cur.moveToFirst();
					curLat = cur.getDouble(1);
					curLng = cur.getDouble(2);
					int latCheck = Utilities.compareGeoPoint(curLat, destLat);
					int lngCheck = Utilities.compareGeoPoint(curLng, destLng);
					if (latCheck == 0 && lngCheck == 0) {
						Log.d("NAVIGATOR", "Reached destination");
						result = true;
					} else {
						Log.d("NAVIGATOR",
								"Not yet reached destination. Keep going!");
						result = false;
					}
				} else {
					Log.d("NAVIGATOR",
							"No Location update available in the database");
					result = false;
				}
				cur.close();
				db.close();
				return result;
			}

		}, "NavigationService");
		navigator.start();
	}

	/**
	 * inital set up before tracking the next target in the schedule list
	 */
	public void setUpNextTarget() {
		// TODO IF user wants to continue the delay without rescheduling,
		// cascade the delay to the next event in schedule as well.
		// TODO might need to decide here on what to do.
		currentEvent = next;
		db.updateEventStatus(currentEvent.getEvent().getId(), "completed");
		if (currentScheduleIndex < schedule.size() - 1) {
			next = schedule.get(++currentScheduleIndex);
			db.updateEventStatus(next.getEvent().getId(), "inprogress");
			startAt = currentEvent.getEvent().getEndTime().getTime();
			reachDestinationAt = next.getStartTime().getTime();
			destLat = next.getEvent().getLocation().getLatitude();
			destLng = next.getEvent().getLocation().getLongitude();
		} else {
			Log.d("NAVIGATOR", "Final destination reached. Hurrayyyyyy!!!!!");
			stopThisThread();
		}
	}

	/**
	 * @return the isRunning
	 */
	public boolean isRunning() {
		return isRunning;
	}

	/**
	 * @param isRunning
	 *            the isRunning to set
	 */
	public void setRunning(boolean isRunning) {
		this.isRunning = isRunning;
	}

	public void stopThisThread() {
		isRunning = false;
		//navigator = null;
	}

	@Override
	public IBinder onBind(Intent arg0) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void onDestroy() {
		//Utilities.setSchedule(null);
		if (tts != null) {
			tts.stop();
			tts.shutdown();
		}
		setRunning(false);
		super.onDestroy();
	}
	
}